const CompileUtil = {
  getVal(vm, expr) {
    return expr.split(".").reduce((data, current) => {
      return data[current];
    }, vm.$data);
  },
  setValue(vm, expr, value) {
    expr.split(".").reduce((data, current, index, arr) => {
      if (arr.length - 1 == index) {
        return (data[current] = value);
      }
      return data[current];
    }, vm.$data);
  },
  model(node, expr, vm) {
    //vm.$data
    let fn = this.updater["modelUpdater"];
    let val = this.getVal(vm, expr);
    new Watcher(vm, expr, (newVal) => {
      fn(node, newVal);
    });
    node.addEventListener("input", (e) => {
      let v = e.target.value;
      this.setValue(vm, expr, v);
    });
    fn(node, val);
  },
  html() {},
  on(node, expr, vm, eventName) {
    node.addEventListener(eventName, (e) => {
      vm[expr] && vm[expr].call(vm, e);
    });
  },
  getContentValue(vm, expr) {
    let reg = /\{\{(.+?)\}\}/g;
    return expr.replace(reg, (...args) => {
      return this.getVal(vm, args[1]);
    });
  },
  text(node, expr, vm) {
    // let reg = /[{{](.+?)[}}]/g;
    let fn = this.updater["textUpdater"];
    let reg = /\{\{(.+?)\}\}/g;
    let content = expr.replace(reg, (...args) => {
      new Watcher(vm, args[1], (newValue) => {
        fn(node, this.getContentValue(vm, expr)); //返回了一个圈的字符串
      });
      return this.getVal(vm, args[1]);
    });
    fn(node, content);
  },
  updater: {
    modelUpdater(node, value) {
      node.value = value;
    },
    htmlUpdater() {},
    textUpdater(node, value) {
      node.textContent = value;
    },
  },
};
class Compiler {
  constructor(el, vm) {
    //判断el属性，是不是一个元素，如果不是元素，就获取
    this.vm = vm;
    this.el = this.isElementNode(el) ? el : document.querySelector(el);
    //把当前节点中的元素，获取到，放到内存中
    let fragment = this.node2fragment(this.el);
    //编译模板 用数据编译
    this.compile(fragment);
    this.el.appendChild(fragment);
  }
  isDirective(attrName) {
    return attrName.startsWith("v-");
  }
  //编译元素
  compileElement(node) {
    const attributes = node.attributes;
    [...attributes].forEach((attr) => {
      let { name, value: expr } = attr;
      //判断是不是指令
      if (this.isDirective(name)) {
        let [, directive] = name.split("-");
        let [directiveName, eventName] = directive.split(":");
        CompileUtil[directiveName](node, expr, this.vm, eventName);
      }
    });
  }
  //编译文本
  compileText(node) {
    //判断当前文本节点中内容是否包含
    let content = node.textContent;
    // let reg = /\{\{(.+?)\}\}/;
    let reg = /[{{](.+?)[}}]/;
    if (reg.test(content)) {
      //console.log(node); //找到所有文本
      CompileUtil["text"](node, content, this.vm);
    }
  }

  compile(node) {
    let childNodes = node.childNodes;
    Array.from(childNodes).forEach((child) => {
      if (this.isElementNode(child)) {
        this.compileElement(child);
        this.compile(child);
      } else {
        this.compileText(child);
      }
    });
  }
  node2fragment(node) {
    let fragment = document.createDocumentFragment();
    let firstChild;
    while ((firstChild = node.firstChild)) {
      fragment.appendChild(firstChild);
    }
    return fragment;
  }
  isElementNode(node) {
    //是不是元素节点
    return node.nodeType === 1;
  }
}
class Vue {
  constructor(opts) {
    this.$el = opts.el;
    this.$data = opts.data();
    //这个根元素存在，编译模板
    let computed = opts.computed;
    let methods = opts.methods;
    if (this.$el) {
      //把数据 全部转化成Object.defineProperty 来定义
      new Observe(this.$data);
      this.proxyVm(this.$data);
      for (let key in computed) {
        Object.defineProperty(this.$data, key, {
          get: () => {
            return computed[key].call(this);
          },
        });
      }
      for (let key in methods) {
        Object.defineProperty(this, key, {
          get: () => {
            return methods[key];
          },
        });
      }
      //把数据获取操作，vm上的取值操作
      new Compiler(this.$el, this);
    }
  }
  proxyVm(data) {
    Object.keys(data).forEach((key) => {
      Object.defineProperty(this, key, {
        get() {
          return data[key];
        },
        set(newVal) {
          data[key] = newVal;
        },
      });
    });
  }
}

class Observe {
  constructor(data) {
    this.observe(data);
  }
  observe(data) {
    if (data && typeof data === "object") {
      //如果是对象
      Object.keys(data).forEach((key) => {
        this.defineRective(data, key, data[key]);
      });
    }
  }
  defineRective(obj, key, value) {
    this.observe(value);
    let dep = new Dep(); //具有发布和订阅的功能
    Object.defineProperty(obj, key, {
      get() {
        //创建watcher时，会去到对应的内容
        Dep.target && dep.addSub(Dep.target);
        return value;
      },
      set: (newVal) => {
        if (newVal === value) {
          return;
        }
        value = newVal;
        if (newVal && typeof newVal) {
          this.observe(newVal);
          dep.notify();
        }
      },
    });
  }
}
//观察者模式

class Watcher {
  constructor(vm, expr, cb) {
    this.vm = vm;
    this.expr = expr;
    this.cb = cb;
    //默认先存放一个老值
    this.oldValue = this.get(vm, expr);
  }
  get() {
    Dep.target = this;
    let value = CompileUtil.getVal(this.vm, this.expr);
    Dep.target = null;
    return value;
  }
  update() {
    //更新操作，数据变化后，会调用观察者的update方法
    let newVal = CompileUtil.getVal(this.vm, this.expr);
    if (newVal !== this.oldValue) {
      this.cb(newVal);
    }
  }
}

class Dep {
  constructor() {
    this.subs = []; //存放所有的watcher
  }
  addSub(watcher) {
    //添加watcher的方法
    this.subs.push(watcher);
  }
  notify() {
    this.subs.forEach((watcher) => watcher.update());
  }
}
